`include "defines.svh"
`include "axi_defines.svh"
`default_nettype none

module axi_arbiter(
    input  logic  clk,
    input  logic  reset,

    // Master 0 Interface
    input  ysyx_data_t m0_debug_pc,
    input  logic  m0_awvalid,
    output logic  m0_awready,
    input  ysyx_addr_t m0_awaddr,

    input  logic  m0_wvalid,
    output logic  m0_wready,
    input  ysyx_data_t m0_wdata,
    input  ysyx_strb_t m0_wstrb,

    output logic  m0_bvalid,
    input  logic  m0_bready,
    output ysyx_resp_t m0_bresp,

    input  logic  m0_arvalid,
    output logic  m0_arready,
    input  ysyx_addr_t m0_araddr,

    output logic  m0_rvalid,
    input  logic  m0_rready,
    output ysyx_data_t m0_rdata,
    output ysyx_resp_t m0_rresp,

    // Master 1 Interface
    input  ysyx_data_t m1_debug_pc,
    input  logic  m1_awvalid,
    output logic  m1_awready,
    input  ysyx_addr_t m1_awaddr,

    input  logic  m1_wvalid,
    output logic  m1_wready,
    input  ysyx_data_t m1_wdata,
    input  ysyx_strb_t m1_wstrb,

    output logic  m1_bvalid,
    input  logic  m1_bready,
    output ysyx_resp_t m1_bresp,

    input  logic  m1_arvalid,
    output logic  m1_arready,
    input  ysyx_addr_t m1_araddr,

    output logic  m1_rvalid,
    input  logic  m1_rready,
    output ysyx_data_t m1_rdata,
    output ysyx_resp_t m1_rresp,

    // Slave Interface
    output ysyx_data_t s_debug_pc,
    output logic  s_awvalid,
    input  logic  s_awready,
    output ysyx_addr_t s_awaddr,

    output logic  s_wvalid,
    input  logic  s_wready,
    output ysyx_data_t s_wdata,
    output ysyx_strb_t s_wstrb,

    input  logic  s_bvalid,
    output logic  s_bready,
    input  ysyx_resp_t s_bresp,

    output logic  s_arvalid,
    input  logic  s_arready,
    output ysyx_addr_t s_araddr,

    input  logic  s_rvalid,
    output logic  s_rready,
    input  ysyx_data_t s_rdata,
    input  ysyx_resp_t s_rresp,

    // extra signals
    input axi_slave_read_in_t m0_extra_rin,
    output axi_slave_read_out_t m0_extra_rout,
    input axi_slave_write_in_t m0_extra_win,
    output axi_slave_write_out_t m0_extra_wout,

    input axi_slave_read_in_t m1_extra_rin,
    output axi_slave_read_out_t m1_extra_rout,
    input axi_slave_write_in_t m1_extra_win,
    output axi_slave_write_out_t m1_extra_wout,

    input axi_master_read_in_t s_extra_rin,
    output axi_master_read_out_t s_extra_rout,
    input axi_master_write_in_t s_extra_win,
    output axi_master_write_out_t s_extra_wout
);

    arb_state_t aw_state, ar_state;

    // Write Address Channel Arbiter
    always_ff @(posedge clk) begin
        case (aw_state)
            IDLE: begin
                if (ar_state != IDLE) aw_state <= IDLE;
                else if (m1_awvalid) aw_state <= MASTER_1;
                else if (m0_awvalid) aw_state <= MASTER_0;
            end
            MASTER_0: begin
                if (s_bready && s_bvalid) aw_state <= IDLE;
            end
            MASTER_1: begin
                if (s_bready && s_bvalid) aw_state <= IDLE;
            end
            default: begin
                aw_state <= IDLE;
            end
        endcase
    end
    // PC Arbiter
    assign s_debug_pc = (aw_state == MASTER_0 || ar_state == MASTER_0) ? m0_debug_pc :
                        (aw_state == MASTER_1 || ar_state == MASTER_1) ? m1_debug_pc : `NULL;


    // Write Address Channel Arbiter
    assign s_awvalid  = (aw_state == MASTER_0) ? m0_awvalid :
                        (aw_state == MASTER_1) ? m1_awvalid : `OFF;
    assign s_awaddr   = (aw_state == MASTER_0) ? m0_awaddr  :
                        (aw_state == MASTER_1) ? m1_awaddr  : `NULL;
    assign m0_awready = (aw_state == MASTER_0) ? s_awready  : `OFF;
    assign m1_awready = (aw_state == MASTER_1) ? s_awready  : `OFF;

    // Write Data Channel Arbiter
    assign s_wvalid  = (aw_state == MASTER_0) ? m0_wvalid :
                       (aw_state == MASTER_1) ? m1_wvalid : `OFF;
    assign s_wdata   = (aw_state == MASTER_0) ? m0_wdata  :
                       (aw_state == MASTER_1) ? m1_wdata  : `NULL;
    assign s_wstrb   = (aw_state == MASTER_0) ? m0_wstrb  :
                       (aw_state == MASTER_1) ? m1_wstrb  : `NULL;
    assign m0_wready = (aw_state == MASTER_0) ? s_wready  : `OFF;
    assign m1_wready = (aw_state == MASTER_1) ? s_wready  : `OFF;

    // Write Response Channel Arbiter
    assign m0_bvalid = (aw_state == MASTER_0) ? s_bvalid  : `OFF;
    assign m1_bvalid = (aw_state == MASTER_1) ? s_bvalid  : `OFF;
    assign m0_bresp  = (aw_state == MASTER_0) ? s_bresp   : `NULL;
    assign m1_bresp  = (aw_state == MASTER_1) ? s_bresp   : `NULL;
    assign s_bready  = (aw_state == MASTER_0) ? m0_bready :
                       (aw_state == MASTER_1) ? m1_bready : `OFF;

    // Write Extra Channel Arbiter
    assign m0_extra_wout = (aw_state == MASTER_0) ? s_extra_win  : `NULL;
    assign m1_extra_wout = (aw_state == MASTER_1) ? s_extra_win  : `NULL;
    assign s_extra_wout  = (aw_state == MASTER_0) ? m0_extra_win :
                           (aw_state == MASTER_1) ? m1_extra_win : `NULL;


    // Read Address Channel Arbiter
    always_ff @(posedge clk) begin
        case (ar_state)
            IDLE: begin
                if (aw_state != IDLE) ar_state <= IDLE;
                else if (m1_arvalid) ar_state <= MASTER_1;
                else if (m0_arvalid) ar_state <= MASTER_0;
            end
            MASTER_0: begin
                if (s_rready && s_rvalid) ar_state <= IDLE;
            end
            MASTER_1: begin
                if (s_rready && s_rvalid) ar_state <= IDLE;
            end
            default: begin
                ar_state <= IDLE;
            end
        endcase
    end

    // Read Address Channel Arbiter
    assign s_arvalid  = (ar_state == MASTER_0) ? m0_arvalid :
                        (ar_state == MASTER_1) ? m1_arvalid : `OFF;
    assign s_araddr   = (ar_state == MASTER_0) ? m0_araddr  :
                        (ar_state == MASTER_1) ? m1_araddr  : `NULL;
    assign m0_arready = (ar_state == MASTER_0) ? s_arready  : `OFF;
    assign m1_arready = (ar_state == MASTER_1) ? s_arready  : `OFF;

    // Read Data Channel Arbiter
    assign m0_rvalid = (ar_state == MASTER_0) ? s_rvalid  : `OFF;
    assign m1_rvalid = (ar_state == MASTER_1) ? s_rvalid  : `OFF;
    assign m0_rdata  = (ar_state == MASTER_0) ? s_rdata   : `NULL;
    assign m1_rdata  = (ar_state == MASTER_1) ? s_rdata   : `NULL;
    assign m0_rresp  = (ar_state == MASTER_0) ? s_rresp   : `NULL;
    assign m1_rresp  = (ar_state == MASTER_1) ? s_rresp   : `NULL;
    assign s_rready  = (ar_state == MASTER_0) ? m0_rready :
                       (ar_state == MASTER_1) ? m1_rready : `OFF;

    // Read Extra Channel Arbiter
    assign m0_extra_rout = (ar_state == MASTER_0) ? s_extra_rin  : `NULL;
    assign m1_extra_rout = (ar_state == MASTER_1) ? s_extra_rin  : `NULL;
    assign s_extra_rout  = (ar_state == MASTER_0) ? m0_extra_rin :
                           (ar_state == MASTER_1) ? m1_extra_rin : `NULL;

endmodule
